home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / info-service / www / src / WWW / Library / Implementation / HTFile.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-29  |  27.6 KB  |  1,070 lines

  1. /*            File Access                HTFile.c
  2. **            ===========
  3. **
  4. **    This is unix-specific code in general, with some VMS bits.
  5. **    These are routines for file access used by browsers.
  6. **
  7. ** History:
  8. **       Feb 91    Written Tim Berners-Lee CERN/CN
  9. **       Apr 91    vms-vms access included using DECnet syntax
  10. **    26 Jun 92 (JFG) When running over DECnet, suppressed FTP.
  11. **            Fixed access bug for relative names on VMS.
  12. **
  13. ** Bugs:
  14. **    FTP: Cannot access VMS files from a unix machine.
  15. **      How can we know that the
  16. **    target machine runs VMS?
  17. */
  18.  
  19. #include "HTFile.h"        /* Implemented here */
  20.  
  21.  
  22. #define INFINITY 512        /* file name length @@ FIXME */
  23. #define MULTI_SUFFIX ".multi"   /* Extension for scanning formats */
  24.  
  25. #include "HTUtils.h"
  26.  
  27. #include "HTParse.h"
  28. #include "tcp.h"
  29. #include "HTTCP.h"
  30. #ifndef DECNET
  31. #include "HTFTP.h"
  32. #endif
  33. #include "HTAnchor.h"
  34. #include "HTAtom.h"
  35. #include "HTWriter.h"
  36. #include "HTFWriter.h"
  37. #include "HTInit.h"
  38. #include "HTBTree.h"
  39.  
  40. typedef struct _HTSuffix {
  41.     char *        suffix;
  42.     HTAtom *    rep;
  43.     HTAtom *    encoding;
  44.     float        quality;
  45. } HTSuffix;
  46.  
  47.  
  48. #ifdef USE_DIRENT        /* Set this for Sys V systems */
  49. #define STRUCT_DIRENT struct dirent
  50. #else
  51. #define STRUCT_DIRENT struct direct
  52. #endif
  53.  
  54. #include "HTML.h"        /* For directory object building */
  55.  
  56. #define PUTC(c) (*target->isa->put_character)(target, c)
  57. #define PUTS(s) (*target->isa->put_string)(target, s)
  58. #define START(e) (*target->isa->start_element)(target, e, 0, 0)
  59. #define END(e) (*target->isa->end_element)(target, e)
  60. #define END_TARGET (*target->isa->end_document)(target)
  61. #define FREE_TARGET (*target->isa->free)(target)
  62. struct _HTStructured {
  63.     CONST HTStructuredClass *    isa;
  64.     /* ... */
  65. };
  66.  
  67.  
  68. /*                   Controlling globals
  69. **
  70. */
  71.  
  72. PUBLIC int HTDirAccess = HT_DIR_OK;
  73. PUBLIC int HTDirReadme = HT_DIR_README_TOP;
  74.  
  75. PRIVATE char *HTMountRoot = "/Net/";        /* Where to find mounts */
  76. #ifdef vms
  77. PRIVATE char *HTCacheRoot = "/WWW$SCRATCH/";   /* Where to cache things */
  78. #else
  79. PRIVATE char *HTCacheRoot = "/tmp/W3_Cache_";   /* Where to cache things */
  80. #endif
  81.  
  82. /* PRIVATE char *HTSaveRoot  = "$(HOME)/WWW/";*/    /* Where to save things */
  83.  
  84.  
  85. /*    Suffix registration
  86. */
  87.  
  88. PRIVATE HTList * HTSuffixes = 0;
  89. PRIVATE HTSuffix no_suffix = { "*", NULL, NULL, 1.0 };
  90. PRIVATE HTSuffix unknown_suffix = { "*.*", NULL, NULL, 1.0};
  91.  
  92.  
  93. /*    Define the representation associated with a file suffix
  94. **    -------------------------------------------------------
  95. **
  96. **    Calling this with suffix set to "*" will set the default
  97. **    representation.
  98. **    Calling this with suffix set to "*.*" will set the default
  99. **    representation for unknown suffix files which contain a ".".
  100. */
  101. PUBLIC void HTSetSuffix ARGS4(
  102.     CONST char *,    suffix,
  103.     CONST char *,    representation,
  104.     CONST char *,    encoding,
  105.     float,        value)
  106. {
  107.     
  108.     HTSuffix * suff;
  109.     
  110.     if (strcmp(suffix, "*")==0) suff = &no_suffix;
  111.     else if (strcmp(suffix, "*.*")==0) suff = &unknown_suffix;
  112.     else {
  113.     suff = (HTSuffix*) calloc(1, sizeof(HTSuffix));
  114.     if (suff == NULL) outofmem(__FILE__, "HTSetSuffix");
  115.     
  116.     if (!HTSuffixes) HTSuffixes = HTList_new();
  117.     HTList_addObject(HTSuffixes, suff);
  118.     
  119.     StrAllocCopy(suff->suffix, suffix);
  120.     }
  121.  
  122.     suff->rep = HTAtom_for(representation);
  123.     
  124.     {
  125.         char * enc = NULL;
  126.     char * p;
  127.     StrAllocCopy(enc, encoding);
  128.     for (p=enc; *p; p++) *p = TOLOWER(*p);
  129.     suff->encoding = HTAtom_for(encoding);
  130.     }
  131.     
  132.     suff->quality = value;
  133. }
  134.  
  135.  
  136.  
  137.  
  138. #ifdef vms
  139. /*    Convert unix-style name into VMS name
  140. **    -------------------------------------
  141. **
  142. ** Bug:    Returns pointer to static -- non-reentrant
  143. */
  144. PRIVATE char * vms_name(CONST char * nn, CONST char * fn)
  145. {
  146.  
  147. /*    We try converting the filename into Files-11 syntax. That is, we assume
  148. **    first that the file is, like us, on a VMS node. We try remote
  149. **    (or local) DECnet access. Files-11, VMS, VAX and DECnet
  150. **    are trademarks of Digital Equipment Corporation. 
  151. **    The node is assumed to be local if the hostname WITHOUT DOMAIN
  152. **    matches the local one. @@@
  153. */
  154.     static char vmsname[INFINITY];    /* returned */
  155.     char * filename = (char*)malloc(strlen(fn)+1);
  156.     char * nodename = (char*)malloc(strlen(nn)+2+1);    /* Copies to hack */
  157.     char *second;        /* 2nd slash */
  158.     char *last;            /* last slash */
  159.     
  160.     char * hostname = HTHostName();
  161.  
  162.     if (!filename || !nodename) outofmem(__FILE__, "vms_name");
  163.     strcpy(filename, fn);
  164.     strcpy(nodename, "");    /* On same node? Yes if node names match */
  165.     {
  166.         char *p, *q;
  167.         for (p=hostname, q=nn; *p && *p!='.' && *q && *q!='.'; p++, q++){
  168.         if (TOUPPER(*p)!=TOUPPER(*q)) {
  169.             strcpy(nodename, nn);
  170.         q = strchr(nodename, '.');    /* Mismatch */
  171.         if (q) *q=0;            /* Chop domain */
  172.         strcat(nodename, "::");        /* Try decnet anyway */
  173.         break;
  174.         }
  175.     }
  176.     }
  177.  
  178.     second = strchr(filename+1, '/');        /* 2nd slash */
  179.     last = strrchr(filename, '/');    /* last slash */
  180.         
  181.     if (!second) {                /* Only one slash */
  182.     sprintf(vmsname, "%s%s", nodename, filename + 1);
  183.     } else if(second==last) {        /* Exactly two slashes */
  184.     *second = 0;        /* Split filename from disk */
  185.     sprintf(vmsname, "%s%s:%s", nodename, filename+1, second+1);
  186.     *second = '/';    /* restore */
  187.     } else {                 /* More than two slashes */
  188.     char * p;
  189.     *second = 0;        /* Split disk from directories */
  190.     *last = 0;        /* Split dir from filename */
  191.     sprintf(vmsname, "%s%s:[%s]%s",
  192.         nodename, filename+1, second+1, last+1);
  193.     *second = *last = '/';    /* restore filename */
  194.     for (p=strchr(vmsname, '['); *p!=']'; p++)
  195.         if (*p=='/') *p='.';    /* Convert dir sep.  to dots */
  196.     }
  197.     free(nodename);
  198.     free(filename);
  199.     return vmsname;
  200. }
  201.  
  202.  
  203. #endif /* vms */
  204.  
  205.  
  206.  
  207. /*    Send README file
  208. **
  209. **  If a README file exists, then it is inserted into the document here.
  210. */
  211.  
  212. #ifdef GOT_READ_DIR
  213. PRIVATE void do_readme ARGS2(HTStructured *, target, CONST char *, localname)
  214.     FILE * fp;
  215.     char * readme_file_name = 
  216.     malloc(strlen(localname)+ 1 + strlen(HT_DIR_README_FILE) + 1);
  217.     strcpy(readme_file_name, localname);
  218.     strcat(readme_file_name, "/");
  219.     strcat(readme_file_name, HT_DIR_README_FILE);
  220.     
  221.     fp = fopen(readme_file_name,  "r");
  222.     
  223.     if (fp) {
  224.     HTStructuredClass targetClass;
  225.     
  226.     targetClass =  *target->isa;    /* (Can't init agregate in K&R) */
  227.     START(HTML_PRE);
  228.     for(;;){
  229.         char c = fgetc(fp);
  230.         if (c == (char)EOF) break;
  231.         switch (c) {
  232.             case '&':
  233.         case '<':
  234.         case '>':
  235.             PUTC('&');
  236.             PUTC('#');
  237.             PUTC((char)(c / 10));
  238.             PUTC((char) (c % 10));
  239.             PUTC(';');
  240.             break;
  241. /*            case '\n':
  242.             PUTC('\r');    
  243. Bug removed thanks to joe@athena.mit.edu */            
  244.         default:
  245.             PUTC(c);
  246.         }
  247.     }
  248.     END(HTML_PRE);
  249.     fclose(fp);
  250.     } 
  251. }
  252. #endif
  253.  
  254.  
  255. /*    Make the cache file name for a W3 document
  256. **    ------------------------------------------
  257. **    Make up a suitable name for saving the node in
  258. **
  259. **    E.g.    /tmp/WWW_Cache_news/1234@cernvax.cern.ch
  260. **        /tmp/WWW_Cache_http/crnvmc/FIND/xx.xxx.xx
  261. **
  262. ** On exit,
  263. **    returns    a malloc'ed string which must be freed by the caller.
  264. */
  265. PUBLIC char * HTCacheFileName ARGS1(CONST char *,name)
  266. {
  267.     char * access = HTParse(name, "", PARSE_ACCESS);
  268.     char * host = HTParse(name, "", PARSE_HOST);
  269.     char * path = HTParse(name, "", PARSE_PATH+PARSE_PUNCTUATION);
  270.     
  271.     char * result;
  272.     result = (char *)malloc(
  273.         strlen(HTCacheRoot)+strlen(access)
  274.         +strlen(host)+strlen(path)+6+1);
  275.     if (result == NULL) outofmem(__FILE__, "HTCacheFileName");
  276.     sprintf(result, "%s/WWW/%s/%s%s", HTCacheRoot, access, host, path);
  277.     free(path);
  278.     free(access);
  279.     free(host);
  280.     return result;
  281. }
  282.  
  283.  
  284. /*    Open a file for write, creating the path
  285. **    ----------------------------------------
  286. */
  287. #ifdef NOT_IMPLEMENTED
  288. PRIVATE int HTCreatePath ARGS1(CONST char *,path)
  289. {
  290.     return -1;
  291. }
  292. #endif
  293.  
  294. /*    Convert filenames between local and WWW formats
  295. **    -----------------------------------------------
  296. **    Make up a suitable name for saving the node in
  297. **
  298. **    E.g.    $(HOME)/WWW/news/1234@cernvax.cern.ch
  299. **        $(HOME)/WWW/http/crnvmc/FIND/xx.xxx.xx
  300. **
  301. ** On exit,
  302. **    returns    a malloc'ed string which must be freed by the caller.
  303. */
  304. PUBLIC char * HTLocalName ARGS1(CONST char *,name)
  305. {
  306.     char * access = HTParse(name, "", PARSE_ACCESS);
  307.     char * host = HTParse(name, "", PARSE_HOST);
  308.     char * path = HTParse(name, "", PARSE_PATH+PARSE_PUNCTUATION);
  309.     
  310.     HTUnEscape(path);    /* Interpret % signs */
  311.     
  312.     if (0==strcmp(access, "file")) {
  313.         free(access);    
  314.     if ((0==strcasecomp(host, HTHostName())) ||
  315.         (0==strcasecomp(host, "localhost")) || !*host) {
  316.         free(host);
  317.         if (TRACE) fprintf(stderr, "Node `%s' means path `%s'\n", name, path);
  318.         return(path);
  319.     } else {
  320.         char * result = (char *)malloc(
  321.                     strlen("/Net/")+strlen(host)+strlen(path)+1);
  322.               if (result == NULL) outofmem(__FILE__, "HTLocalName");
  323.         sprintf(result, "%s%s%s", "/Net/", host, path);
  324.         free(host);
  325.         free(path);
  326.         if (TRACE) fprintf(stderr, "Node `%s' means file `%s'\n", name, result);
  327.         return result;
  328.     }
  329.     } else {  /* other access */
  330.     char * result;
  331.         CONST char * home =  (CONST char*)getenv("HOME");
  332.     if (!home) home = "/tmp"; 
  333.     result = (char *)malloc(
  334.         strlen(home)+strlen(access)+strlen(host)+strlen(path)+6+1);
  335.       if (result == NULL) outofmem(__FILE__, "HTLocalName");
  336.     sprintf(result, "%s/WWW/%s/%s%s", home, access, host, path);
  337.     free(path);
  338.     free(access);
  339.     free(host);
  340.     return result;
  341.     }
  342. }
  343.  
  344.  
  345. /*    Make a WWW name from a full local path name
  346. **
  347. ** Bugs:
  348. **    At present, only the names of two network root nodes are hand-coded
  349. **    in and valid for the NeXT only. This should be configurable in
  350. **    the general case.
  351. */
  352.  
  353. PUBLIC char * WWW_nameOfFile ARGS1 (CONST char *,name)
  354. {
  355.     char * result;
  356. #ifdef NeXT
  357.     if (0==strncmp("/private/Net/", name, 13)) {
  358.     result = (char *)malloc(7+strlen(name+13)+1);
  359.     if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
  360.     sprintf(result, "file://%s", name+13);
  361.     } else
  362. #endif
  363.     if (0==strncmp(HTMountRoot, name, 5)) {
  364.     result = (char *)malloc(7+strlen(name+5)+1);
  365.     if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
  366.     sprintf(result, "file://%s", name+5);
  367.     } else {
  368.         result = (char *)malloc(7+strlen(HTHostName())+strlen(name)+1);
  369.     if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
  370.     sprintf(result, "file://%s%s", HTHostName(), name);
  371.     }
  372.     if (TRACE) fprintf(stderr, "File `%s'\n\tmeans node `%s'\n", name, result);
  373.     return result;
  374. }
  375.  
  376.  
  377. /*    Determine a suitable suffix, given the representation
  378. **    -----------------------------------------------------
  379. **
  380. ** On entry,
  381. **    rep    is the atomized MIME style representation
  382. **
  383. ** On exit,
  384. **    returns    a pointer to a suitable suffix string if one has been
  385. **        found, else "".
  386. */
  387. PUBLIC CONST char * HTFileSuffix ARGS1(HTAtom*, rep)
  388. {
  389.     HTSuffix * suff;
  390.     int n;
  391.     int i;
  392.  
  393. #ifndef NO_INIT    
  394.     if (!HTSuffixes) HTFileInit();
  395. #endif
  396.     n = HTList_count(HTSuffixes);
  397.     for(i=0; i<n; i++) {
  398.     suff = HTList_objectAt(HTSuffixes, i);
  399.     if (suff->rep == rep) {
  400.         return suff->suffix;        /* OK -- found */
  401.     }
  402.     }
  403.     return "";        /* Dunno */
  404. }
  405.  
  406.  
  407. /*    Determine file format from file name
  408. **    ------------------------------------
  409. **
  410. **    This version will return the representation and also set
  411. **    a variable for the encoding.
  412. **
  413. **    It will handle for example  x.txt, x.txt,Z, x.Z
  414. */
  415.  
  416. PUBLIC HTFormat HTFileFormat ARGS2 (
  417.             CONST char *,    filename,
  418.             HTAtom **,    pencoding)
  419.  
  420. {
  421.     HTSuffix * suff;
  422.     int n;
  423.     int i;
  424.     int lf = strlen(filename);
  425.  
  426. #ifndef NO_INIT    
  427.     if (!HTSuffixes) HTFileInit();
  428. #endif
  429.     *pencoding = NULL;
  430.     n = HTList_count(HTSuffixes);
  431.     for(i=0; i<n; i++) {
  432.         int ls;
  433.     suff = HTList_objectAt(HTSuffixes, i);
  434.     ls = strlen(suff->suffix);
  435.     if ((ls <= lf) && 0==strcmp(suff->suffix, filename + lf - ls)) {
  436.         int j;
  437.         *pencoding = suff->encoding;
  438.         if (suff->rep) return suff->rep;        /* OK -- found */
  439.         
  440.         for(j=0; j<n; j++) {  /* Got encoding, need representation */
  441.         int ls2;
  442.         suff = HTList_objectAt(HTSuffixes, j);
  443.         ls2 = strlen(suff->suffix);
  444.         if ((ls <= lf) && 0==strncmp(
  445.             suff->suffix, filename + lf - ls -ls2, ls2)) {
  446.             if (suff->rep) return suff->rep;
  447.         }
  448.         }
  449.         
  450.     }
  451.     }
  452.     
  453.     /* defaults tree */
  454.     
  455.     suff = strchr(filename, '.') ?     /* Unknown suffix */
  456.          ( unknown_suffix.rep ? &unknown_suffix : &no_suffix)
  457.      : &no_suffix;
  458.      
  459.     /* set default encoding unless found with suffix already */
  460.     if (!*pencoding) *pencoding = suff->encoding ? suff->encoding
  461.                     : HTAtom_for("binary");
  462.     return suff->rep ? suff->rep : WWW_BINARY;
  463. }
  464.  
  465.  
  466. /*    Determine value from file name
  467. **    ------------------------------
  468. **
  469. */
  470.  
  471. PUBLIC float HTFileValue ARGS1 (CONST char *,filename)
  472.  
  473. {
  474.     HTSuffix * suff;
  475.     int n;
  476.     int i;
  477.     int lf = strlen(filename);
  478.  
  479. #ifndef NO_INIT    
  480.     if (!HTSuffixes) HTFileInit();
  481. #endif
  482.     n = HTList_count(HTSuffixes);
  483.     for(i=0; i<n; i++) {
  484.         int ls;
  485.     suff = HTList_objectAt(HTSuffixes, i);
  486.     ls = strlen(suff->suffix);
  487.     if ((ls <= lf) && 0==strcmp(suff->suffix, filename + lf - ls)) {
  488.         if (TRACE) fprintf(stderr, "File: Value of %s is %.3f\n",
  489.                    filename, suff->quality);
  490.         return suff->quality;        /* OK -- found */
  491.     }
  492.     }
  493.     return 0.3;        /* Dunno! */
  494. }
  495.  
  496.  
  497. /*    Determine write access to a file
  498. **    --------------------------------
  499. **
  500. ** On exit,
  501. **    return value    YES if file can be accessed and can be written to.
  502. **
  503. ** Bugs:
  504. **    1.    No code for non-unix systems.
  505. **    2.    Isn't there a quicker way?
  506. */
  507.  
  508. #ifdef vms
  509. #define NO_GROUPS
  510. #endif
  511. #ifdef NO_UNIX_IO
  512. #define NO_GROUPS
  513. #endif
  514. #ifdef PCNFS
  515. #define NO_GROUPS
  516. #endif
  517.  
  518. PUBLIC BOOL HTEditable ARGS1 (CONST char *,filename)
  519. {
  520. #ifdef NO_GROUPS
  521.     return NO;        /* Safe answer till we find the correct algorithm */
  522. #else
  523.     int     groups[NGROUPS];    
  524.     uid_t    myUid;
  525.     int        ngroups;            /* The number of groups  */
  526.     struct stat    fileStatus;
  527.     int        i;
  528.         
  529.     if (stat(filename, &fileStatus))        /* Get details of filename */
  530.         return NO;                /* Can't even access file! */
  531.  
  532.     ngroups = getgroups(NGROUPS, groups);    /* Groups to which I belong  */
  533.     myUid = geteuid();                /* Get my user identifier */
  534.  
  535.     if (TRACE) {
  536.         int i;
  537.     fprintf(stderr, 
  538.         "File mode is 0%o, uid=%d, gid=%d. My uid=%d, %d groups (",
  539.             (unsigned int) fileStatus.st_mode, fileStatus.st_uid,
  540.         fileStatus.st_gid,
  541.         myUid, ngroups);
  542.     for (i=0; i<ngroups; i++) fprintf(stderr, " %d", groups[i]);
  543.     fprintf(stderr, ")\n");
  544.     }
  545.     
  546.     if (fileStatus.st_mode & 0002)        /* I can write anyway? */
  547.         return YES;
  548.     
  549.     if ((fileStatus.st_mode & 0200)        /* I can write my own file? */
  550.      && (fileStatus.st_uid == myUid))
  551.         return YES;
  552.  
  553.     if (fileStatus.st_mode & 0020)        /* Group I am in can write? */
  554.     {
  555.        for (i=0; i<ngroups; i++) {
  556.             if (groups[i] == fileStatus.st_gid)
  557.             return YES;
  558.     }
  559.     }
  560.     if (TRACE) fprintf(stderr, "\tFile is not editable.\n");
  561.     return NO;                    /* If no excuse, can't do */
  562. #endif
  563. }
  564.  
  565.  
  566. /*    Make a save stream
  567. **    ------------------
  568. **
  569. **    The stream must be used for writing back the file.
  570. **    @@@ no backup done
  571. */
  572. PUBLIC HTStream * HTFileSaveStream ARGS1(HTParentAnchor *, anchor)
  573. {
  574.  
  575.     CONST char * addr = HTAnchor_address((HTAnchor*)anchor);
  576.     char *  localname = HTLocalName(addr);
  577.     
  578.     FILE* fp = fopen(localname, "w");
  579.     if (!fp) return NULL;
  580.     
  581.     return HTFWriter_new(fp);
  582.     
  583. }
  584.  
  585. /*      Output one directory entry
  586. **
  587. */
  588. PUBLIC void HTDirEntry ARGS3(HTStructured *, target,
  589.          CONST char * , tail,
  590.          CONST char *,  entry)
  591. {
  592.     char * relative;
  593.     char * escaped = HTEscape(entry, URL_XPALPHAS);
  594.  
  595.     /* If empty tail, gives absolute ref below */
  596.     relative = (char*) malloc(
  597.                   strlen(tail) + strlen(escaped)+2);
  598.     if (relative == NULL) outofmem(__FILE__, "DirRead");
  599.     sprintf(relative, "%s/%s", tail, escaped);
  600.     HTStartAnchor(target, "", relative);
  601.     free(escaped);
  602.     free(relative);
  603.     PUTS(entry);
  604.     END(HTML_A);
  605. }
  606.  
  607. /*      Output parent directory entry
  608. **
  609. **    This gives the TITLE and H1 header, and also a link
  610. **    to the parent directory if appropriate.
  611. */
  612. PUBLIC void HTDirTitles ARGS2(HTStructured *, target,
  613.          HTAnchor * , anchor)
  614.  
  615. {
  616.     char * logical = HTAnchor_address(anchor);
  617.     char * path = HTParse(logical, "", PARSE_PATH + PARSE_PUNCTUATION);
  618.     char * current;
  619.  
  620.     current = strrchr(path, '/');    /* last part or "" */
  621.     free(logical);
  622.  
  623.     {
  624.       char * printable = NULL;
  625.       StrAllocCopy(printable, (current + 1));
  626.       HTUnEscape(printable);
  627.       START(HTML_TITLE);
  628.       PUTS(*printable ? printable : "Welcome ");
  629.       PUTS(" directory");
  630.       END(HTML_TITLE);    
  631.     
  632.       START(HTML_H1);
  633.       PUTS(*printable ? printable : "Welcome");
  634.       END(HTML_H1);
  635.       free(printable);
  636.     }
  637.  
  638.     /*  Make link back to parent directory
  639.      */
  640.  
  641.     if (current && current[1]) {   /* was a slash AND something else too */
  642.         char * parent;
  643.     char * relative;
  644.     *current++ = 0;
  645.       parent = strrchr(path, '/');  /* penultimate slash */
  646.  
  647.     relative = (char*) malloc(strlen(current) + 4);
  648.     if (relative == NULL) outofmem(__FILE__, "DirRead");
  649.     sprintf(relative, "%s/..", current);
  650.     HTStartAnchor(target, "", relative);
  651.     free(relative);
  652.  
  653.     PUTS("Up to ");
  654.     if (parent) {
  655.       char * printable = NULL;
  656.       StrAllocCopy(printable, parent + 1);
  657.       HTUnEscape(printable);
  658.       PUTS(printable);
  659.       free(printable);
  660.     } else {
  661.       PUTS("/");
  662.     }
  663.  
  664.     END(HTML_A);
  665.  
  666.     }
  667.     free(path);
  668. }
  669.         
  670.  
  671.  
  672. /*    Load a document
  673. **    ---------------
  674. **
  675. ** On entry,
  676. **    addr        must point to the fully qualified hypertext reference.
  677. **            This is the physsical address of the file
  678. **
  679. ** On exit,
  680. **    returns        <0        Error has occured.
  681. **            HTLOADED    OK 
  682. **
  683. */
  684. PUBLIC int HTLoadFile ARGS4 (
  685.     CONST char *,        addr,
  686.     HTParentAnchor *,    anchor,
  687.     HTFormat,        format_out,
  688.     HTStream *,        sink
  689. )
  690. {
  691.     char * filename;
  692.     HTFormat format;
  693.     int fd = -1;        /* Unix file descriptor number = INVALID */
  694.     char * nodename = 0;
  695.     char * newname=0;    /* Simplified name of file */
  696.     HTAtom * encoding;    /* @@ not used yet */
  697.     
  698. /*    Reduce the filename to a basic form (hopefully unique!)
  699. */
  700.     StrAllocCopy(newname, addr);
  701.     filename=HTParse(newname, "", PARSE_PATH|PARSE_PUNCTUATION);
  702.     nodename=HTParse(newname, "", PARSE_HOST);
  703.     free(newname);
  704.     
  705.     format = HTFileFormat(filename, &encoding);
  706.  
  707.  
  708. #ifdef vms
  709. /* Assume that the file is in Unix-style syntax if it contains a '/'
  710.    after the leading one @@ */
  711.     {
  712.     char * vmsname = strchr(filename + 1, '/') ?
  713.       vms_name(nodename, filename) : filename + 1;
  714.     fd = open(vmsname, O_RDONLY, 0);
  715.     
  716. /*    If the file wasn't VMS syntax, then perhaps it is ultrix
  717. */
  718.     if (fd<0) {
  719.         char ultrixname[INFINITY];
  720.         if (TRACE) fprintf(stderr, "HTFile: Can't open as %s\n", vmsname);
  721.         sprintf(ultrixname, "%s::\"%s\"", nodename, filename);
  722.         fd = open(ultrixname, O_RDONLY, 0);
  723.         if (fd<0) {
  724.         if (TRACE) fprintf(stderr, 
  725.                    "HTFile: Can't open as %s\n", ultrixname);
  726.         }
  727.     }
  728.     }
  729. #else
  730.  
  731.     free(filename);
  732.     
  733. /*    For unix, we try to translate the name into the name of a transparently
  734. **    mounted file.
  735. **
  736. **    Not allowed in secure (HTClienntHost) situations TBL 921019
  737. */
  738. #ifndef NO_UNIX_IO
  739.     /*  Need protection here for telnet server but not httpd server */
  740.      
  741.     if (!HTSecure) {        /* try local file system */
  742.     char * localname = HTLocalName(addr);
  743.     struct stat dir_info;
  744.     
  745. #ifdef GOT_READ_DIR
  746.  
  747. /*              Multiformat handling
  748. **
  749. **    If needed, scan directory to find a good file.
  750. **  Bug:  we don't stat the file to find the length
  751. */
  752.     if ( (strlen(localname) > strlen(MULTI_SUFFIX))
  753.        && (0==strcmp(localname + strlen(localname) - strlen(MULTI_SUFFIX),
  754.                       MULTI_SUFFIX))) {
  755.         DIR *dp;
  756.  
  757.         STRUCT_DIRENT * dirbuf;
  758.         float best = NO_VALUE_FOUND;    /* So far best is bad */
  759.         HTFormat best_rep = NULL;    /* Set when rep found */
  760.         STRUCT_DIRENT best_dirbuf;    /* Best dir entry so far */
  761.  
  762.         char * base = strrchr(localname, '/');
  763.         int baselen;
  764.  
  765.         if (!base || base == localname) goto forget_multi;
  766.         *base++ = 0;        /* Just got directory name */
  767.         baselen = strlen(base)- strlen(MULTI_SUFFIX);
  768.         base[baselen] = 0;    /* Chop off suffix */
  769.  
  770.         dp = opendir(localname);
  771.         if (!dp) {
  772. forget_multi:
  773.         free(localname);
  774.         return HTLoadError(sink, 500,
  775.             "Multiformat: directory scan failed.");
  776.         }
  777.         
  778.         while (dirbuf = readdir(dp)) {
  779.             /* while there are directory entries to be read */
  780.         if (dirbuf->d_ino == 0) continue;
  781.                 /* if the entry is not being used, skip it */
  782.         
  783.         if (dirbuf->d_namlen > baselen &&      /* Match? */
  784.             !strncmp(dirbuf->d_name, base, baselen)) {    
  785.             HTFormat rep = HTFileFormat(dirbuf->d_name, &encoding);
  786.             float value = HTStackValue(rep, format_out,
  787.                             HTFileValue(dirbuf->d_name),
  788.                         0.0  /* @@@@@@ */);
  789.             if (value != NO_VALUE_FOUND) {
  790.                 if (TRACE) fprintf(stderr,
  791.                 "HTFile: value of presenting %s is %f\n",
  792.                 HTAtom_name(rep), value);
  793.             if  (value > best) {
  794.                 best_rep = rep;
  795.                 best = value;
  796.                 best_dirbuf = *dirbuf;
  797.                }
  798.             }    /* if best so far */             
  799.          } /* if match */  
  800.             
  801.         } /* end while directory entries left to read */
  802.         closedir(dp);
  803.         
  804.         if (best_rep) {
  805.         format = best_rep;
  806.         base[-1] = '/';        /* Restore directory name */
  807.         base[0] = 0;
  808.         StrAllocCat(localname, best_dirbuf.d_name);
  809.         goto open_file;
  810.         
  811.         } else {             /* If not found suitable file */
  812.         free(localname);
  813.         return HTLoadError(sink, 403,    /* List formats? */
  814.            "Could not find suitable representation for transmission.");
  815.         }
  816.         /*NOTREACHED*/
  817.     } /* if multi suffix */
  818. /*
  819. **    Check to see if the 'localname' is in fact a directory.  If it is
  820. **    create a new hypertext object containing a list of files and 
  821. **    subdirectories contained in the directory.  All of these are links
  822. **      to the directories or files listed.
  823. **      NB This assumes the existance of a type 'STRUCT_DIRENT', which will
  824. **      hold the directory entry, and a type 'DIR' which is used to point to
  825. **      the current directory being read.
  826. */
  827.     
  828.     
  829.     if (stat(localname,&dir_info) == -1) {     /* get file information */
  830.                                    /* if can't read file information */
  831.         if (TRACE) fprintf(stderr, "HTFile: can't stat %s\n", localname);
  832.  
  833.     }  else {        /* Stat was OK */
  834.         
  835.  
  836.         if (((dir_info.st_mode) & S_IFMT) == S_IFDIR) {
  837.         /* if localname is a directory */    
  838.  
  839.         HTStructured* target;        /* HTML object */
  840.         HTStructuredClass targetClass;
  841.  
  842.         DIR *dp;
  843.         STRUCT_DIRENT * dirbuf;
  844.         
  845.         char * logical;
  846.         char * tail;
  847.         
  848.         BOOL present[HTML_A_ATTRIBUTES];
  849.         
  850.         char * tmpfilename = NULL;
  851.         struct stat file_info;
  852.         
  853.         if (TRACE)
  854.             fprintf(stderr,"%s is a directory\n",localname);
  855.             
  856. /*    Check directory access.
  857. **    Selective access means only those directories containing a
  858. **    marker file can be browsed
  859. */
  860.         if (HTDirAccess == HT_DIR_FORBID) {
  861.             free(localname);
  862.             return HTLoadError(sink, 403,
  863.             "Directory browsing is not allowed.");
  864.         }
  865.  
  866.  
  867.         if (HTDirAccess == HT_DIR_SELECTIVE) {
  868.             char * enable_file_name = 
  869.             malloc(strlen(localname)+ 1 +
  870.              strlen(HT_DIR_ENABLE_FILE) + 1);
  871.             strcpy(enable_file_name, localname);
  872.             strcat(enable_file_name, "/");
  873.             strcat(enable_file_name, HT_DIR_ENABLE_FILE);
  874.             if (stat(enable_file_name, &file_info) != 0) {
  875.             free(localname);
  876.             return HTLoadError(sink, 403,
  877.             "Selective access is not enabled for this directory");
  878.             }
  879.         }
  880.  
  881.  
  882.         dp = opendir(localname);
  883.         if (!dp) {
  884.             free(localname);
  885.             return HTLoadError(sink, 403, "This directory is not readable.");
  886.         }
  887.  
  888.  
  889.  /*    Directory access is allowed and possible
  890.  */
  891.         logical = HTAnchor_address((HTAnchor*)anchor);
  892.         tail = strrchr(logical, '/') +1;    /* last part or "" */
  893.         
  894.         target = HTML_new(anchor, format_out, sink);
  895.         targetClass = *target->isa;    /* Copy routine entry points */
  896.             
  897.           { int i;
  898.             for(i=0; i<HTML_A_ATTRIBUTES; i++)
  899.                 present[i] = (i==HTML_A_HREF);
  900.         }
  901.         
  902.                 HTDirTitles(target, (HTAnchor *)anchor);
  903.  
  904.                 if (HTDirReadme == HT_DIR_README_TOP)
  905.             do_readme(target, localname);
  906.         {
  907.             HTBTree * bt = HTBTree_new((HTComparer)strcasecomp);
  908.  
  909.             while (dirbuf = readdir(dp))
  910.             {
  911.                 HTBTElement * dirname = NULL;
  912.  
  913.                 /* while there are directory entries to be read */
  914.                 if (dirbuf->d_ino == 0)
  915.                   /* if the entry is not being used, skip it */
  916.                 continue;
  917.             
  918.  
  919.                 /* if the current entry is parent directory */
  920.             if ((*(dirbuf->d_name)=='.') ||
  921.                 (*(dirbuf->d_name)==','))
  922.                 continue;    /* skip those files whose name begins
  923.                         with '.' or ',' */
  924.  
  925.             dirname = (HTBTElement *)malloc(
  926.                     strlen(dirbuf->d_name) + 2);
  927.             if (dirname == NULL) outofmem(__FILE__,"DirRead");
  928.             StrAllocCopy(tmpfilename,localname);
  929.             if (strcmp(localname,"/")) 
  930.  
  931.                     /* if filename is not root directory */
  932.                 StrAllocCat(tmpfilename,"/"); 
  933.  
  934.  
  935.             StrAllocCat(tmpfilename,dirbuf->d_name);
  936.             stat(tmpfilename, &file_info);
  937.             if (((file_info.st_mode) & S_IFMT) == S_IFDIR)
  938.                         sprintf((char *)dirname,"D%s",dirbuf->d_name);
  939.             else sprintf((char *)dirname,"F%s",dirbuf->d_name);
  940.                 /* D & F to have first directories, then files */
  941.             HTBTree_add(bt,dirname); /* Sort dirname in the tree bt */
  942.             }
  943.  
  944.             /*    Run through tree printing out in order
  945.              */
  946.             {
  947.                 HTBTElement * next_element = HTBTree_next(bt,NULL);
  948.                 /* pick up the first element of the list */
  949.             char state;
  950.                 /* I for initial (.. file),
  951.                    D for directory file,
  952.                    F for file */
  953.             
  954.             state = 'I';
  955.  
  956.             while (next_element != NULL)
  957.                 {
  958.                 StrAllocCopy(tmpfilename,localname);
  959.                 if (strcmp(localname,"/")) 
  960.  
  961.                     /* if filename is not root directory */
  962.                     StrAllocCat(tmpfilename,"/"); 
  963.  
  964.                 StrAllocCat(tmpfilename,
  965.                     (char *)HTBTree_object(next_element)+1);
  966.                 /* append the current entry's filename to the path */
  967.                 HTSimplify(tmpfilename);
  968.                 /* Output the directory entry */
  969.                 if (strcmp((char *)
  970.                          (HTBTree_object(next_element)),"D.."))
  971.                 {                
  972.                 if (state != *(char *)(HTBTree_object(next_element))) 
  973.                 {
  974.                     if (state == 'D')
  975.                         END(HTML_DIR);
  976.                     state = *(char *)
  977.                         (HTBTree_object(next_element))=='D'?'D':'F';
  978.                     START(HTML_H2);
  979.                     PUTS(state == 'D'?"Subdirectories:":"Files");
  980.                     END(HTML_H2);
  981.                     START(HTML_DIR);
  982.                 }
  983.                     START(HTML_LI);
  984.                 }
  985.                 HTDirEntry(target, tail,
  986.                        (char*)HTBTree_object(next_element) +1);
  987.  
  988.                 next_element = HTBTree_next(bt,next_element);
  989.                     /* pick up the next element of the list; 
  990.                  if none, return NULL*/
  991.             }
  992.             if (state == 'I')
  993.             {
  994.                 START(HTML_P);
  995.                 PUTS("Empty Directory");
  996.             }
  997.             else
  998.                 END(HTML_DIR);
  999.             }
  1000.  
  1001.                 /* end while directory entries left to read */
  1002.             closedir(dp);
  1003.             free(logical);
  1004.             free(tmpfilename);
  1005.             HTBTreeAndObject_free(bt);
  1006.  
  1007.             if (HTDirReadme == HT_DIR_README_BOTTOM)
  1008.               do_readme(target, localname);
  1009.             END_TARGET;
  1010.             FREE_TARGET;
  1011.             free(localname);
  1012.             return HT_LOADED;    /* document loaded */
  1013.         }
  1014.  
  1015.         } /* end if localname is directory */
  1016.     
  1017.     } /* end if file stat worked */
  1018.     
  1019. /* End of directory reading section
  1020. */
  1021. #endif
  1022. open_file:
  1023.     {
  1024.         FILE * fp = fopen(localname,"r");
  1025.         if(TRACE) fprintf (stderr, "HTFile: Opening `%s' gives %p\n",
  1026.                 localname, (void*)fp);
  1027.         if (fp) {        /* Good! */
  1028.         if (HTEditable(localname)) {
  1029.             HTAtom * put = HTAtom_for("PUT");
  1030.             HTList * methods = HTAnchor_methods(anchor);
  1031.             if (HTList_indexOf(methods, put) == (-1)) {
  1032.             HTList_addObject(methods, put);
  1033.             }
  1034.         }
  1035.         free(localname);
  1036.         HTParseFile(format, format_out, anchor, fp, sink);
  1037.         fclose(fp);
  1038.         return HT_LOADED;
  1039.         }  /* If succesfull open */
  1040.     }    /* scope of fp */
  1041.     }  /* local unix file system */    
  1042. #endif
  1043. #endif
  1044.  
  1045. #ifndef DECNET
  1046. /*    Now, as transparently mounted access has failed, we try FTP.
  1047. */
  1048.     {
  1049.     if (strcmp(nodename, HTHostName())!=0)
  1050.         return HTFTPLoad(addr, anchor, format_out, sink);
  1051.     }
  1052. #endif
  1053.  
  1054. /*    All attempts have failed.
  1055. */
  1056.     {
  1057.         if (TRACE)
  1058.         printf("Can't open `%s', errno=%d\n", addr, errno);
  1059.     return HTLoadError(sink, 403, "Can't access requested file.");
  1060.     }
  1061.     
  1062.  
  1063. }
  1064.  
  1065. /*        Protocol descriptors
  1066. */
  1067. PUBLIC HTProtocol HTFTP  = { "ftp", HTLoadFile, 0 };
  1068. PUBLIC HTProtocol HTFile = { "file", HTLoadFile, HTFileSaveStream };
  1069.